/**
 * 创建并加载配置数据状态，其中包括：
 * - map 用于快速找到配置数据的从数据库中查询出来的原始数据项主键与原始数据项之间的映射
 * - list 用于全部展示的去除目录数据后的数据列表
 * - tree 用于树形展示和选择的树形结构的数据列表
 * - dirTree 用于父级目录选择的只包含目录类型的树形结构的数据列表
 * - treeMapAll 用于获取指定目录下的所有子级(包含子目录和子配置)的目录配置数据项主键与目录配置数据项之间的映射
 * - treeMap 用于获取指定目录下的子级配置列表的目录配置数据项主键与目录配置数据项之间的映射
 *
 * @template M 从数据库中查询出来的原始数据项主键与原始数据项之间的映射的类型
 * @template L 用于全部展示的去除目录数据后的数据列表的类型
 * @template T 用于树形展示和选择的树形结构的数据列表的类型
 * @template DT 用于父级目录选择的只包含目录类型的树形结构的数据列表的类型
 * @template TM 用于获取指定目录下的子级配置列表的目录配置数据项主键与目录配置数据项之间的映射的类型
 * @param {() => Promise<ResultType>} getData 从数据库中查询数据的方法
 * @param {(getMap: () => M, getList: () => L, getTree: () => T, getDirTree: () => DT, getTreeMap: () => TM) => void} otherHandler
 * load 中的自定义其他处理
 */
export const genAndLoadConfStatus = <M, L, T, DT, TMA, TM>(
  getData: () => Promise<ResultType>,
  otherHandler: (
    getMap: () => M,
    getList: () => L,
    getTree: () => T,
    getDirTree: () => DT,
    getTreeMapAll: () => TMA,
    getTreeMap: () => TM
  ) => void = () => {}
) => {
  // 用于快速找到配置数据的从数据库中查询出来的原始数据项主键与原始数据项之间的映射
  const [map, getMap] = StatusUtil.genStatus<M>({} as M)
  // 用于全部展示的去除目录数据后的数据列表
  const [list, getList] = StatusUtil.genStatus<L>([] as L)
  // 用于树形展示和选择的树形结构的数据列表
  const [tree, getTree] = StatusUtil.genStatus<T>([] as T)
  // 用于父级目录选择的只包含目录类型的树形结构的数据列表
  const [dirTree, getDirTree] = StatusUtil.genStatus<DT>([] as DT)
  // 用于获取指定目录下的所有子级(包含子目录和子配置)的目录配置数据项主键与目录配置数据项之间的映射
  const [treeMapAll, getTreeMapAll] = StatusUtil.genStatus<TMA>({} as TMA)
  // 用于获取指定目录下的子级配置列表的目录配置数据项主键与目录配置数据项之间的映射
  const [treeMap, getTreeMap] = StatusUtil.genStatus<TM>({} as TM)

  const load = async (): Promise<void> => {
    ElLoadingGlobalOpen()

    const res = await getData()
    if (!res.status || !res.data || res.data.length === 0) {
      ElLoadingGlobalClose()
      return
    }

    // 拷贝一份数据，避免后续转化为树形结构的相关操作与原始数据互相影响
    const data = ArrUtil.copyDeep(res.data)

    // 用于快速找到配置数据的从数据库中查询出来的原始数据项主键与原始数据项之间的映射
    map.value = ArrUtil.convert2Map(res.data, '_id') as M

    // 由于 map 与 list 之间的数据操作会造成的影响为：多了两个字段、icon 数据值的转化，且 list 的过滤操作是生成一个
    // 新数组，可以视为没有互相影响，因此共用一个原始数据，减少空间消耗，同时减少重复操作

    // 用于全部展示的去除目录数据后的数据列表
    list.value = res.data
      .filter((item) => {
        if (isDirConf(item)) return false
        item.value = item?._id
        item.label = item?.name
        return true
      })
      .sort((a, b) => b?.priority - a?.priority)

    // 向拷贝的数据列表中添加默认目录
    const dirDefault = {
      _id: DirIdConst.Default,
      type: DirConfTypeConst,
      name: '默认目录',
      priority: Number.MAX_SAFE_INTEGER,
      icon: genFloderIcon()
    }
    data.push(dirDefault)
    // 没有父级目录的配置将其父级目录设置为默认目录
    for (const item of data) {
      if (!isDirConf(item) && !map.value[item?.parent]) item.parent = DirIdConst.Default
    }
    // 转化为树形结构
    const convert2TreeRes = ArrUtil.convert2Tree(data, {
      primaryFieldName: '_id',
      isClearTag: true
    })

    // 用于树形展示和选择的树形结构的数据列表
    tree.value = ArrUtil.copyDeep(convert2TreeRes.tree, {
      propsMap: {
        ['_id']: ['_id', 'value'],
        ['name']: ['count', 'name', 'label']
      },
      targetPropValPreHandler: {
        count: (item) => `(${ArrUtil.count(item?.children ?? [], (item) => !isDirConf(item))})`
      },
      isCopyObjEmptyArrProp: false,
      sort: (a, b) => b?.priority - a?.priority
    }) as T

    // 由于 dirTree 会修改 children 属性，将其中不为目录类型的子级排除，所以 tree 与 dirTree 之间，需要拷贝出一份
    // 新数据，避免互相影响，同时采用拷贝的方式，可以避免重复操作

    // 用于父级目录选择的只包含目录类型的树形结构的数据列表
    dirTree.value = ArrUtil.copyDeep(tree.value as any[], {
      excludeArrItem: (item) => !isDirConf(item)
    }) as DT

    // 用于获取指定目录下的所有子级(包含子目录和子配置)的目录配置数据项主键与目录配置数据项之间的映射
    treeMapAll.value = ObjUtil.copyDeep(convert2TreeRes.map) as TMA

    // 由于 tree 和 dirTree 都是从 convert2TreeRes tree 拷贝出来的新数据，且 convert2TreeRes 在数据操作处理完
    // 成后，会被回收，此时后续的操作不会对前面的数据造成影响，所以这里直接复用 convert2TreeRes map

    // 用于获取指定目录下的子级配置列表的目录配置数据项主键与目录配置数据项之间的映射
    for (const id in convert2TreeRes.map) {
      const item = convert2TreeRes.map[id]
      // 将子级列表中的子级目录数据排除，确保通过映射获取的数据中只有指定目录下的子级配置列表
      if (item?.children?.length) {
        item.children = item.children
          .filter((item) => {
            if (isDirConf(item)) return false
            item.value = item?._id
            item.label = item?.name
            return true
          })
          .sort((a, b) => b?.priority - a?.priority)
      }
    }
    treeMap.value = convert2TreeRes.map as TM

    otherHandler(getMap, getList, getTree, getDirTree, getTreeMapAll, getTreeMap)

    ElLoadingGlobalClose()
  }

  return {
    map,
    getMap,
    list,
    getList,
    tree,
    getTree,
    dirTree,
    getDirTree,
    treeMapAll,
    getTreeMapAll,
    treeMap,
    getTreeMap,
    load: FunUtil.throttleByLimit(load)
  }
}
